#
#  controller.py
#  flickrmirror
#
#  Created by Nathan Van Gheem on 5/19/09.
#  Copyright (c) 2009 __MyCompanyName__. All rights reserved.
#

#import local libs to be able to use
import sys, time, os, threading, datetime

#now add the path so we have the right libraries to use...
path = '/'.join(os.path.abspath( __file__ ).split('/')[:-1])
sys.path.append(os.path.join(path, 'libs'))

from objc import YES, NO, IBAction, IBOutlet
from Foundation import *
from AppKit import *
from PyObjCTools import AppHelper

from flickrmirror.controller import MirrorController
from flickrmirror.settings import pickled_foldername
from flickrmirror.tools import logger

class controller(NSWindowController):
    
	setsTableView = IBOutlet()
	mirrorLocation = IBOutlet()
	loadingSign = IBOutlet()
	loadingText = IBOutlet()
	flickrInfo = IBOutlet()
	progressBar = IBOutlet()
	photoProgressBar = IBOutlet()
	_mirror_controller = None
	tabView = IBOutlet()
	
	authenticateButton = IBOutlet()
	loadSettingsButton = IBOutlet()
	stopButton = IBOutlet()
	syncButton = IBOutlet()
	mirrorButton = IBOutlet()
	currentPhotoDownloadDetailsText = IBOutlet()
	overallDownloadDetailsText = IBOutlet()
	authenticatedText = IBOutlet()
	current_sender = None
	cancel_operation = False
	
	name = u"flickrmirror"
		
	def applicationDidFinishLaunching_(self, sender):
		logger.info("flickrmirror has finished loading")
		
	def applicationWillTerminate_(self, sender):
		logger.info("flickrmirror terminating")
		self.cancel_operation = True
		if self.has_mirror_loaded():
			logger.info("flickrmirror saving before terminating")
			self.status("saving flickrmirror settings before exiting.")
			self.mirror_controller.save()
		
	def input(self, prompt):
		alert = NSAlert.alloc().init()
		alert.addButtonWithTitle_(u"Go!")
		alert.setMessageText_("Wait one moment...")
		alert.setInformativeText_(prompt)
		alert.setAlertStyle_(NSWarningAlertStyle)
		input = NSTextField.alloc().initWithFrame_(NSMakeRect(0, 0, 200, 24))
		input.setStringValue_("")
		alert.setAccessoryView_(input)
		button = alert.runModal()
		input.validateEditing()
		return input.stringValue()
		
	def prompt(self, msg, result=True):
		if result:
			return self.input(msg)
		else:
			alert = NSAlert.alloc().init()
			alert.addButtonWithTitle_(u"OK")
			alert.setMessageText_("Wait one moment...")
			alert.setInformativeText_(msg)
			alert.setAlertStyle_(NSWarningAlertStyle)
			alert.runModal()
		
	def status(self, msg, nl=False):
		logger.info(msg)
		self.loadingText.setStringValue_(msg)
	
	def start_loading(self, sender):
		self.current_sender = sender
		self.loadingSign.startAnimation_(sender)
		self.authenticateButton.setEnabled_(NO)
		self.loadSettingsButton.setEnabled_(NO)
		self.mirrorButton.setEnabled_(NO)
		self.setsTableView.setEnabled_(NO)
		self.syncButton.setEnabled_(NO)
		self.stopButton.setEnabled_(YES)
		
	def stop_loading(self, sender):
		self.loadingSign.stopAnimation_(sender)
		self.authenticateButton.setEnabled_(YES)
		self.loadSettingsButton.setEnabled_(YES)
		self.mirrorButton.setEnabled_(YES)
		self.setsTableView.setEnabled_(YES)
		self.syncButton.setEnabled_(YES)
		self.stopButton.setEnabled_(NO)
		self.stopButton.setTitle_(u"Stop!")
		self.cancel_operation = False
						
	def get_mc(self):
		if self._mirror_controller is None:
			self._mirror_controller = MirrorController(self)
			
		return self._mirror_controller
		
	mirror_controller = property(get_mc)
	
	def get_mirror_directory_from_user(self, sender):
		panel = NSOpenPanel.openPanel()
		panel.setCanChooseDirectories_(YES)
		panel.setAllowsMultipleSelection_(NO)
		panel.setCanChooseFiles_(NO)
		panel.setTitle_(u"Choose the directory where you want to store your flickrmirror")
		
		returnCode = panel.runModal()
		
		if returnCode:
			folder = panel.filenames()[0]
			self.mirrorLocation.setStringValue_(folder)
			self.mirror_controller.backup_directory = folder
			
			if os.path.exists(os.path.join(folder, pickled_foldername)):
				self.status(u"Existing flickr settings present in this mirror location.")
			else:
				self.status(u"No existing flickr settings file present.  You will need to load from scratch.")
		else:
			raise Exception("Must select directory.")

	def has_mirror_location(self):
		if not self.mirrorLocation.stringValue():
			return False
		else:
			return True
	
	def authenticated(self):
		return self.mirror_controller.authenticator.authenticated()

	def has_mirror_loaded(self):
		return self.mirror_controller.mirror_model is not None

	def update_last_update(self):
		if self.mirror_model.last_updated > 0:
			dt = datetime.datetime.fromtimestamp(self.mirror_model.last_updated)
			self.view.status("last updated on %s" % dt.strftime("%Y-%m-%d %H:%M:%S"))
		else:
			#never updated
			pass

	def run_action(self, precondition, condition_message, start_msg, end_msg, error_msg, run_method, sender):
		if precondition:
			self.prompt(condition_message, False)
			return
			
		controller = self
		class T(threading.Thread):
			def run(self):
				try:
					run_method()
					controller.status(end_msg)
				except Exception, inst:
					logger.error(inst.message)
					controller.status(error_msg)
				finally:
					controller.stop_loading(sender)
					
		self.start_loading(sender)
		self.status(start_msg)
		t = T()
		t.start()
	
	@IBAction
	def stopButtonClicked_(self, sender):
		self.cancel_operation = True
		self.stopButton.setTitle_(u"Stopping...")

	@IBAction
	def authenticateButtonClicked_(self, sender):
		def run_method():
			self.mirror_controller.authenticate()
			if self.authenticated():
				self.authenticatedText.setStringValue_(u"YES")
			else:
				self.authenticatedText.setStringValue_(u"NO")
	
		self.run_action(
			False, 
			u"Not going to happen", 
			u"Authenticating...",
			u"Authenticated.",
			u"An error occurred while attempting to authenticate with flickr.com",
			run_method,
			sender
		)

	@IBAction
	def loadButtonClicked_(self, sender):
		def run_method():
			self.get_mirror_directory_from_user(sender)
			
			self.mirror_controller.load()
			self.setsTableView.reloadData()
	
		self.run_action(
			not self.authenticated(), 
			u"Must authenticate before you can set your flickrmirror location.", 
			u"Selecting and loading flickrmirror...",
			u"Finished Loading.",
			u"An error occurred while attempting to load flickr mirror settings.",
			run_method,
			sender
		)
		
	@IBAction
	def syncButtonClicked_(self, sender):
		def run_method():
			if self.mirror_controller.mirror_model is None:
				self.mirror_controller.load()
				self.setsTableView.reloadData()
			
			self.mirror_controller.synchronize_server()
			self.setsTableView.reloadData()
			self.stop_loading(sender)
	
		self.run_action(
			not self.has_mirror_location() or not self.authenticated(), 
			u"Must set flickrmirror location and authenticate before you can synchronize with flickr.com", 
			u"Syncing...",
			u"Finished Sync.",
			u"An error occurred while attempting to sync.",
			run_method,
			sender
		)
		
	@IBAction
	def mirrorClicked_(self, sender):
	
		def report_hook(x, y, z):
			if self.cancel_operation:
				raise Exception("Exiting download...")
			res = int((float(x*y)/z)*100)
			if res > 100:
				res = 100
			self.photoProgressBar.setIntValue_(res)
			self.currentPhotoDownloadDetailsText.setStringValue_("%i%% - %.1f/%.1fmb" % (res, float(x*y)/1024.0/1024.0, float(z)/1024.0/1024.0))
			
		def run_method():
			if self.mirror_controller.mirror_model is None:
				self.mirror_controller.load()
				self.setsTableView.reloadData()
			
			dirty_photos = self.mirror_controller.get_dirty_photos()
			self.update_flickr_info(len(dirty_photos), 0)
			
			for i in range(0, len(dirty_photos)):
				if self.cancel_operation:
					self.mirror_controller.save()
					self.stop_loading(sender)
					return
				
				photo, set = dirty_photos[i]
				self.status("%i/%i - Downloading photo %s from set %s." % (i+1, len(dirty_photos), photo.title, set.title) )
				self.mirror_controller.retrieve_photo(photo, set, report_hook)
				self.update_flickr_info(len(dirty_photos), i+1)
			
			self.mirror_controller.save()
	
		self.run_action(
			not self.has_mirror_location() or not self.authenticated(), 
			u"Must select location and authenticate before you can load your flickr data.", 
			u"Mirroring...",
			u"Finished Mirror.",
			u"Mirroring has stopped.",
			run_method,
			sender
		)
		
	def update_flickr_info(self, num_dirty_photos, current_photo_number):
		"""
		sets : # of sets
		total photos : # of photos
		photos to be updated : 
		"""
		pc = self.mirror_controller.get_photo_count()

		self.flickrInfo.setStringValue_(
"""Number of sets : %i
Total number of photos : %i
Total photos needing update : %i""" % (
			len(self.mirror_controller.mirror_model.filtered_sets()),
			pc,
			num_dirty_photos
			)
		)
		percent_downloaded =  int( (float(current_photo_number)/float(num_dirty_photos))*100)
			
		self.progressBar.setIntValue_(percent_downloaded)
		self.overallDownloadDetailsText.setStringValue_("%i%%" % (percent_downloaded))
		self.photoProgressBar.setIntValue_(0)
		self.currentPhotoDownloadDetailsText.setStringValue_(u"...")
		
	def numberOfRowsInTableView_(self, tableView):
		"""
		Delegate method for data source
		Retrieves number of sets in the mirror
		"""
		if self.mirror_controller.mirror_model is None:
			return 0
		else:
			return len(self.mirror_controller.mirror_model.sets)
			
	def tableView_objectValueForTableColumn_row_(self, sender, tableColumn, row):
		"""
		Delegate method for data source
		Retrieves a cell value
		"""
		headerText = tableColumn.headerCell().title()
		set = self.mirror_controller.mirror_model.sets[row]
		
		if headerText == "Enabled":
			return set.id not in self.mirror_controller.mirror_model.filtered
		elif headerText == "id":
			return set.id
		elif headerText == "# photos":
			return set.number_of_photos_and_videos
		else:
			return set.title
		
	def tableView_setObjectValue_forTableColumn_row_(self, sender, enabled, tableColumn, row):
		"""
		Delegate the sets a cell value
		"""
		headerText = tableColumn.headerCell().title()

		if headerText == "Enabled":
			set = self.mirror_controller.mirror_model.sets[row]
			if enabled:
				if set.id in self.mirror_controller.mirror_model.filtered:
					self.mirror_controller.unfilter_set(set.id)
			else:
				if set.id not in self.mirror_controller.mirror_model.filtered:
					self.mirror_controller.filter_set(set.id)
		
	
